/**
    tags.c - The tag extraction subsystem.  Opaquely extracts the tags from
    files, abstracting away the details of matching a given format with its
    handler.
    Copyright (C) 2009  Abram Magner

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "strfuncs.h"
#include "list.h"
#include "plugin.h"
#include "woofs_plugin.h"
#include "tags.h"

//#define TAGS_DEBUG

static int tag_set_fill (void *, char *, char *);
static list_node *tag_set_fetch (tag_set *, char *);
static tag *tag_new (char *, char *);
static void tag_free (tag *);

//=============================================================================
// Harvest the tags from the given file.
//=============================================================================
tag_set *extract_tags (char *filename, plugin_set *pset)
{
  int fd = 0;
  format_plugin *handler = NULL;
  tag_set *tset = NULL;

  if ((fd = open (filename, O_RDONLY)) < 0) return (NULL);
  
  // Find a match for the given file in the set of format plugins.
  handler = plugin_claimfile (pset, fd, filename);
  // Unable to handle the given file.
  if (handler == NULL)
    {
      fprintf (stderr, "Handler was null.\n");
      fprintf (stderr, "File was %s\n", filename);
      close (fd);
      return (NULL);
    }  
  tset = tag_set_new ();
  if (tset == NULL)
    {
      close (fd);
      return (NULL);
    }
  // Use the handler to extract tags.
  handler->plugin_struct->get_tags (fd, filename, tset, tag_set_fill);
  close (fd);

  // Return the set of tags.
  return (tset);
}


//=============================================================================
// Insert the name/value pair into the tag set.  This is for use by plugins.
//=============================================================================
static int tag_set_fill (void *tset, char *name, char *value)
{
  tag_set *set = tset;
  if (tset == NULL) return (-1);
  return (tag_set_insert (set, name, value));
}


//=============================================================================
// Create an empty tag set.
//=============================================================================
tag_set *tag_set_new (void)
{
  return (new_list ());
}

//=============================================================================
// Clean up the given tag set.
//=============================================================================
void tag_set_free (tag_set *ts)
{
  list_node *iter = NULL;

  iter = list_get_iter (ts);
  while (list_iter_hasmore (iter))
    {
      tag_free (iter->data);
      list_iter_advance (iter);
    }
  free_list (ts);
}

//=============================================================================
// Insert the given name/value pair into the tag set.
//=============================================================================
int tag_set_insert (tag_set *ts, char *name, char *value)
{
  tag *t = tag_new (name, value);
  if (t == NULL) return (-1);
  if (tag_set_fetch (ts, name) || list_insert (ts, ts->length, t) < 0)
    {
      tag_free (t);
      return (-1);
    }
  return (0);
}

//=============================================================================
// Return a newly allocated string containing the value associated with name.
//=============================================================================
char *tag_set_lookup (tag_set *ts, char *name)
{
  list_node *node = tag_set_fetch (ts, name);
  if (node == NULL) return (NULL);
  return (strdup (((tag *)node->data)->value));
}

//=============================================================================
// Remove and destroy the given name/value pair from the tag set.
//=============================================================================
/*
void tag_set_remove (tag_set *ts, char *name)
{
  // TODO: Implement me!
}*/

//=============================================================================
// Fetch the list node with the given name.  This should only be used by
// the tag_set functions.
//=============================================================================
static list_node *tag_set_fetch (tag_set *ts, char *name)
{
  list_node *iter = list_get_iter (ts);

  while (list_iter_hasmore (iter))
    {
      tag *t = iter->data;
      if (strcmp (t->name, name) == 0) return (iter);
      list_iter_advance (iter);
    }
  return (NULL);
}

//=============================================================================
// Create a new tag binding name to val.
//=============================================================================
static tag *tag_new (char *name, char *val)
{
  tag *retval = NULL;
  if ((retval = malloc (sizeof (tag))) == NULL) return (NULL);
  retval->name = strdup (name);
  if (retval->name == NULL)
    {
      free (retval);
      return (NULL);
    }
  retval->value = strdup (val);
  if (retval->value == NULL)
    {
      free (retval->name);
      free (retval);
      return (NULL);
    }
  return (retval);
}

//=============================================================================
// Clean up the given tag.
//=============================================================================
static void tag_free (tag *t)
{
  free (t->name);
  free (t->value);
  free (t);
}

#ifdef TAGS_DEBUG
int main (int argc, char **argv)
{
  return (0);
}
#endif
